This notebook will report the findings of creating a decision tree model on a binary version of the Boston housing dataset. For reference:
Note that in this dataset, an instance has a median house value of 1 if the median house value is greater than 230k and 0 if it is not.
First we must load the dataset.
housing<-read.table("housing.header.binary.txt", header=TRUE, sep=",")
colnames(housing)<-c("Crim", "Zn", "Indus", "Chas", "Nox", "Rm", "Age", "Dis", "Rad", "Tax", "Ptratio", "B", "Lstat", "Medv")
summary(housing)
We will use the Crim index and number of rooms in a building as independent variables to train an RPART decision tree that predicts whether the median house value is 0 or 1.
library(rpart)
fit<-rpart(Medv~Crim+Rm, method="class", data=housing)
summary(fit)
Next we visualize the tree:
plot(fit, uniform=TRUE, main="Decision Tree")
text(fit, use.n=TRUE, all=TRUE, cex=.8)
The above decision tree looks at each instance and performs the following splits:
Root node-Is Rm<6.546?
312 instances were split as negatives, and 194 were split as positives.
Traversing the left subtree, we ask:
Is Rm < 6.143?
290 instances were split as negatives, and 72 were split as positives.
Continuing down the left subtree, we have a class label of 0 in response to an answer of 0 to the above question. Here we have 203 instances that were true negatives and 24 that were not.
Traversing the right subtree from the above node, we ask:
Is Crim>=3.621?
87 instances were split as negatives, and 48 were split as positives.
Moving left from here, we have a class label of 0 in response to an answer of 0 to the above question. Here we have 44 instances that were true negatives and 3 that were not.
Traversing the right subtree from the above node, we ask:
Is Rm > 6.365?
43 instances were split as negatives, and 45 were split as positives.
Traversing the left subtree from this node, we ask:
Is Crim >=0.0483?
27 instances were split as negatives, and 20 were split as positives.
Moving right from here, we have a class label of 1 in response to an answer of 1 to the above question. Here we have 5 instances that were true positives and 3 that were not.
The above described process can be used for each decision node to eventually reach a leaf node.
Now we will use all variables to train an RPART decision tree that predicts whether the median house value is 0 or 1. We will use 80% of the data to fit the tree, and 20% of the data to test the effectiveness of the decision tree model.
set.seed(1)
train<-sample(1:nrow(housing), 0.8*nrow(housing))
train.set<-housing[train,]
test.set<-housing[-train,]
housing.tree<-rpart(Medv~Crim+Zn+Indus+Chas+Nox+Rm+Age+Dis+Rad+Tax+Ptratio+B+Lstat, data=housing[train,], method="class")
summary(housing.tree)
housing.pred<-predict(housing.tree, housing[-train,], type="class")
table(housing[-train,]$Medv, housing.pred)
From this, we have that:
True Positive Rate=24/(24+7)=24/31=0.7742
False Positive Rate=9/(9+62)=9/71=0.1268
Accuracy: (62+24)/(62+24+9+7)=86/102=84.31%
Next we show the ROC curve of the classifier which plots true positve rate as a function of false positve rate:
housing.prob<-predict(housing.tree,housing[-train, ], type="prob")
housing.prob.label<-cbind(housing.prob[, 2], housing[-train,]$Medv)
library(ROCR)
pred<-prediction(housing.prob.label[, 1], housing.prob.label[, 2])
perf<-performance(pred, "tpr", "fpr")
plot(perf, col="red")
abline(0,1, col="lightgray")
Next we show the AUC value:
auc<-performance(pred, "auc")
auc
auc@y.values[[1]]
Next we can create a new instance on which to perform a prediction:
ninst<-c(0.03, 13, 3.5, 0.3, 0.58, 4.1, 68, 4.98, 3, 225, 17, 396, 7.56)
names(ninst)<-c("Crim", "Zn", "Indus", "Chas", "Nox", "Rm", "Age", "Dis", "Rad", "Tax", "Ptratio", "B", "Lstat")
ninst.frame<-data.frame(ninst)
plot(housing.tree, uniform=TRUE, main="Decision Tree")
text(housing.tree, use.n=TRUE, all=TRUE, cex=.8)
Using this decison tree on the new instance, we get a class label of 0.